昨日我們過目Service Worker的生命週期後,今天第一步,在專案/public,底下建立一個service-worker的js檔案。
檔案名稱可以隨自己開心沒有規定,接著,我們在/src/js資療夾底下,建立app.js,提供index.html中使用的JS檔案。
if('serviceWorker' in navigator){
navigator.serviceWorker
.register('/service-worker.js')
.then(function(){
console.log('Service Worker 註冊成功');
}).catch(function(error) {
console.log('Service worker 註冊失敗:', error);
});
} else {
console.log('瀏覽器不支援');
}
接著打開app.js輸入上面這段程式,就完成註冊了,一整個很簡單
。
解讀起來就是詢問瀏覽器(navigator)中是否存在(in)名為「serviceWorker」的物件存在,如果瀏覽器支援Service Worker,就會進入「navigator.serviceWorker.register(檔案路徑)」,檔案路徑「/檔案名稱」,/是確保路徑都是從root開始看,從專案的架構,就是指/public/底下的範圍。
service worker的檔案放置的位置會影響,能掌管的範圍。
舉例來說,預設的掌管範圍放在根目錄底下的service worker,
可以存取到的範圍為整個網站:
如果檔案存放在「/public/Product/service-worker.js」,
能控制的範圍為Produc資料夾範圍內的頁面:
透過scope參數,可以加限制存取範圍
//假設這行是在「/Product/Detail.html」頁面執行
navigator.serviceWorker.register('/sw.js', {scope: './'})
「./」依照註冊Service Worker的頁面索在資料夾為基準,意思就是只能存取「/Product/」底下的所有資源。
Service Worker回傳會promise,關於promise會再後面幾篇中再做介紹。
在網頁載入的時候,先不考慮Service Worker對於開發者最注重的就是畫面資料的顯示速度,而今天就算我們網站有使用Service Worker,在使用者感受上,他並不會有任何感受,因為都是運行在背景上。
而假如今天我們網頁有很多圖片、CSS和JS資源要載入,是必我們就必須分配CPU和記憶體來執行,雖然現在手機規格越來越好,但我們還是必須假設頻寬是有一定限制,既然Service Worker對使用者感受上不會有任何反應,我們等頁面資源全部載入後再註冊也不遲。
因此,就將上面的註冊方法改寫了一下,讓資源等待網頁載入後,再註冊Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js');
});
}
但是否該同時載入,還是依照情境再做決定,並沒有強制說一定要使用哪一個。
在app.js註冊Service Worker後,可以透過install事件,來捕捉網頁上有沒有成功註冊Service Worker。
Service Worker只會註冊一次,當更新了Service Worker的檔案後,重新瀏覽頁面會被視為新的Service Worker。self.addEventListener('install', function(event){
console.log('[SW] 安裝(Install) Service Worker!',event);
});
self是一個語法糖,讓我們可以存取Service Worker的背景程式,有寫過網頁的應該都覺得addEventListener這個關鍵字很熟悉,但是在Service Worker裡面,是無法使用click之類我們平常所使用的事件,因為再前一天說過,Service Worker是一套運行再背景的程式,是沒有權限能存取DOM的,所以理所當然能操作DOM的事件在這邊都是無法使用的。

接著我們運行網站後,開F12/Console視窗,可以看到成功監聽到註冊的訊息,代表成功抓到Service Worker的檔案了。
再安裝完之後,Service Worker就可以執行功能性的需求(如:推播)。
self.addEventListener('activate', function(event){
console.log('[SW] 觸發(Activate) Service Worker!',event);
return self.clients.claim();
});
接著新增監聽activate,clients.claim(),是一個控制住,不受控的資源的方法,看官網用了一個Demo介紹了這個方法。
它的意思是,當你打開頁面的時候一開始你會註冊Service Worker,但是你並還沒有接收到圖檔的需求,因為設定3秒的Timeout才發出dog.svg的需求,所以需求並沒有經過Service Worker,這時候你再f5刷新畫面一次,觸發Fetch事件的時候,頁面和圖檔就經過Service Worker的時候,原本是狗的圖就被替換成貓了。
事件後執行,會發現console視窗,只出現註冊及安裝的訊息。
Service Worker 註冊成功
[SW] 安裝(Install) Service Worker! InstallEvent {isTrusted: true, type: "install", target: ServiceWorkerGlobalScope, currentTarget: ServiceWorkerGlobalScope, eventPhase: 2, …}
那是因為假如我們瀏覽器上已經有一個Service Worker已經註冊了,現在我們更新了新版的Service Worker上去後,他會等待舊的Service Worker終止後,才執行新的Service Worker,為了方便Debug,Chrome在開發者工具(F12)上,有一個Application區塊,有Service Workers的狀態區,讓我們可以清楚現在網頁上的狀況。

如上圖,綠色#68401的點是我們第一次在測試install事件時註冊的Service Worker,
而橘色#68402是在加了activate事件後的Service Worker。
在Application/Service Workers上,有三個方式可以解決這個問題。
Service Worker),最右邊**skipWaiting**的連結,它可以跳過等待時間直接強制執行新的Service Worker。Update on reload**打勾,意思是每次重整時,都重新註冊Service Worker。
如果在沒有使用第2-3點的情境下,你修改了新的Service Worker,當前一次的localhost還掛在頁面上,再建置後,開啟新的localhost頁面時候,會發現舊的Service Worker一直掛在上面,那是因為有舊的分頁沒關掉造成的,如果要更新就要將全部localhost的分頁關掉再進入網站,才會載入新的Service Worker。
關於Service Worker
install 事件是第一個會觸發的事件,而且再頁面上只會觸發一次。Fetch不會通過Service Worker,除非Service Worker已經install,且再一次刷新頁面才會有效果。clients.claim()強制控制沒有控制到的資源。關於 Service Worker更新時間
Service Worker的頁面的時候。Service Worker的程式有改變的時候,他會觸發install事件,並進入activate中等待,可以透過skipWaiting強制更新。Service Worker再執行install的時候失敗,會被刪除,舊的Service Worker仍然會繼續執行。Service Worker安裝後,會處於等待狀態(wait),當舊的Service Worker沒有控制任何資源的時候,才會刪除舊的觸發新的。MDN:SW.register()
Clients.claim()
The Service Worker Lifecycle
https://github.com/DakHarry/30day-pwas-practice